package ga;

import java.util.ArrayList;
import java.util.Iterator;

import ga.GA.OpAdi;
import ga.GA.OpComp;
import ga.GA.OpMult;
import ga.GA.OpUna;
import ga.GA.Operacion;

public class ObjTipo {
	
	public enum TipoObjTipo{INT,BOOLEAN,array,puntero,registro,referencia,procedimiento,fallo};
	
	//constructoras
	//constructora por defecto
	public ObjTipo() {
		tipo=TipoObjTipo.fallo;
		error=new Error();
	}
	
	public ObjTipo(String err){
		tipo=TipoObjTipo.fallo;
		error=new Error(err);
	}
	
	public ObjTipo(ObjTipo obj1, ObjTipo obj2) {
		if(obj1!=null && obj2!=null){
			error = new Error(obj1.error, obj2.error);
			if(obj1.tipo != obj2.tipo){
				error.errores().add("Error: No concuerdan los tipos");
				tipo = null;
			}
			else {
				tipo = obj1.tipo;
			}
		}
		else{
			if(obj1 == null && obj2 != null)
				error = new Error("Error con "+ obj2.tipo);
			if(obj2 == null && obj1 != null)
				error = new Error("Error con "+ obj1.tipo);
			if(obj2 == null && obj1 == null)
				error = new Error("Error en objTipo: Son null");
			tipo = TipoObjTipo.fallo;
		}
	}
	
	public ObjTipo(TipoObjTipo tipo, int tam) {
		this.tamanio = tam;
		this.tipo = tipo;
		this.error = new Error();		
	}
		
	public ObjTipo(ObjTipo obj1, TipoObjTipo obj2){
		error = obj1.error;
		if(obj1.tipo != obj2){
			error.errores().add("Error: No concuerdan los tipos");
			tipo = TipoObjTipo.fallo;
		}
		else tipo = obj1.tipo;
	}
	
	public ObjTipo(TipoObjTipo tipo, int tam, ArrayList<ObjCampo> campos){
		this.tipo=tipo;
		this.tamanio = tam;
		this.error = new Error();
		val = new ArrayList<Object>();
		val.add(false);
		val.add(false);
		val.add(false);
		val.add(false);
		val.set(1,campos);
	}
	
	public ObjTipo(TipoObjTipo tipo, String ident, int tam) {
		this.tipo = tipo;
		tamanio = tam;
		error = new Error();
		val = new ArrayList<Object>();
		val.add(false);
		val.add(false);
		val.add(false);
		val.add(false);
		val.set(3, ident);
	}
	
	public ObjTipo(TipoObjTipo tipo, ObjTipo tipoBase, int tam) {
		this.tipo = tipo;
		tamanio = tam;
		error = new Error();
		val = new ArrayList<Object>();
		val.add(false);
		val.add(false);
		val.add(false);
		val.add(false);
		val.set(0, tipoBase);
	}
	
	public ObjTipo(TipoObjTipo tipo, ArrayList<Parametro> parametros, int d){
		this.tipo = tipo;
		this.error = new Error();
		val = new ArrayList<Object>();
		val.add(false);
		val.add(false);
		val.add(false);
		val.add(false);
		val.set(2, parametros);
		this.dir = d;
	}
	
	//accesoras y mutadoras	
	public TipoObjTipo getTipo(){
		return tipo;
	}
	
	public ObjTipo getTipoArray(){
		return (ObjTipo)val.get(0);
	}
	
	public ObjTipo getTipoBasePuntero(){
		return (ObjTipo)val.get(0);
	}
	
	public void setTipoBasePuntero(ObjTipo tBase){
		val.set(0, tBase);
	}
	
	public int getNumElem() {
		return numElem;
	}
	
	public Error getError() {
		return error;
	}
	
	public String getIdenReferencia(){
		return (String)val.get(3);
	}
	
	public ObjTipo getTipoCampoReg(String id){
		ObjCampo obj;
		ArrayList<ObjCampo> campo = (ArrayList<ObjCampo>)val.get(1);
		Iterator<ObjCampo> iter = campo.iterator();
		while(iter.hasNext()){
			obj = iter.next();
			if(obj.getId().equals(id))
				return obj.getTipo();
		}
		return new ObjTipo("Error con getTipoCampoReg de: "+id);
	}
	
	public ArrayList<Parametro> getParams(){
		return (ArrayList<Parametro>)val.get(2);
	}
	
	public int getNumParams(){
		return ((ArrayList<Parametro>)val.get(2)).size();
	}
	
	public int getDirProc(){
		return dir;
	}
		
	public int getTam(){
		return tamanio;
	}
	
	public boolean hayError() {
		  return error.hayError();
	}
	
	public String toString() {
		return "{Error de tipo: "+error+". Mensajes: "+error.errores()+"}";  
	}
	
	public static ObjTipo tipoOpBin(OpComp ope,ObjTipo t1,ObjTipo t2){
		if((t1.getTipo() == TipoObjTipo.INT && t2.getTipo() == TipoObjTipo.INT))
			return new ObjTipo(TipoObjTipo.BOOLEAN,1);
		else
			return new ObjTipo("El operador Comp necesita dos enteros");
	}
	
	public static ObjTipo tipoOpBin(OpAdi ope,ObjTipo t1,ObjTipo t2){
		if((t1.getTipo() == TipoObjTipo.INT && t2.getTipo() == TipoObjTipo.INT) || (t1.getTipo() == TipoObjTipo.BOOLEAN && t2.getTipo() == TipoObjTipo.BOOLEAN))
			return new ObjTipo(t1,t2);
		else 
			return new ObjTipo("El operador Adi requiere que los dos elementos sean del mismo tipo");
	}
	
	public static ObjTipo tipoOpBin(OpMult ope,ObjTipo t1,ObjTipo t2){
		if(t1.getTipo() == TipoObjTipo.INT && t2.getTipo() == TipoObjTipo.INT || (t1.getTipo() == TipoObjTipo.BOOLEAN && t2.getTipo() == TipoObjTipo.BOOLEAN))
			return new ObjTipo(t1,t2);
		else 
			return new ObjTipo("El operador Mult requiere que los dos elementos sean del mismo tipo");
	}
	
	public static ObjTipo tipoOpUn(OpUna opUn,ObjTipo t1){
		if(opUn.op().val() == Operacion.NOT && t1.getTipo() == TipoObjTipo.BOOLEAN)
			return new ObjTipo(t1,TipoObjTipo.BOOLEAN);
		else if(opUn.op().val() == Operacion.MENOS && t1.getTipo() == TipoObjTipo.INT)
				return new ObjTipo(t1,TipoObjTipo.INT);
		else 
			return new ObjTipo("Error con el operador Unario");		
	}
		
	public int desplazamientoEnReg(String IDEN){
		int despl =0;
		ArrayList<ObjCampo> camp = (ArrayList<ObjCampo>)val.get(1);
		for(int t=0; t<camp.size();t++){
			ObjCampo obj = camp.get(t);
			if(obj.getId().equals(IDEN))
				despl = obj.getDesp();
		}
		return despl;
	}
	
	//variables privadas
	private Error error;
	private TipoObjTipo tipo; 
	private ArrayList<Object> val;
	private int tamanio;
	private int numElem;
	private int dir;
}